package com.newtouch.cq.lang.util;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.baomidou.mybatisplus.annotation.IEnum;
import com.newtouch.cq.lang.annotation.InitEnumToMapAn;
import com.newtouch.cq.lang.em.BaseExceptionEnum;
import com.newtouch.cq.lang.exception.MccBizException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * 枚举工具类
 *
 * @author alex
 * @Date: 2021/3/4
 */
public class EnumUtils extends EnumUtil {
    private static Map<String,Map<Object,String>> enumMaps = MapUtil.newHashMap();

    private static Log log = LogFactory.get(EnumUtils.class);

    private static final String BASE_PACKAGE = "com.newtouch.cq";
    private static final String RESOURCE_PATTERN = "/**/em/*.class";

    public static void init() {
        //spring工具类，可以获取指定路径下的全部类
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        try {
            String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    ClassUtils.convertClassNameToResourcePath(BASE_PACKAGE) + RESOURCE_PATTERN;
            Resource[] resources = resourcePatternResolver.getResources(pattern);
            //MetadataReader 的工厂类
            MetadataReaderFactory readerfactory = new CachingMetadataReaderFactory(resourcePatternResolver);
            for (Resource resource : resources) {
                //用于读取类信息
                MetadataReader reader = readerfactory.getMetadataReader(resource);
                //扫描到的class
                String classname = reader.getClassMetadata().getClassName();
                Class<?> clazz = Class.forName(classname);
                //判断是否有指定主解
                InitEnumToMapAn anno = clazz.getAnnotation(InitEnumToMapAn.class);
                if (anno != null) {
                    //将注解中的类型值作为key，对应的类作为 value
                    String name = anno.name();
                    enumMaps.putAll(analysisEnum(name,clazz));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info(JSONUtil.toJsonStr(enumMaps));
    }

    /**
     * 通过名称查询对应的枚举map
     * @param name
     * @return
     */
    public static Map getEnumMapByName(String name) {
        if(enumMaps.isEmpty()) {
            init();
        }
        if(StrUtil.equals("all",name)) {
            return enumMaps;
        }

        return enumMaps.get(name);
    }

    /**
     * 通过枚举的类和value值得到相应的枚举，没有则抛出异常
     * @param clazz
     * @param v
     * @param <E>
     * @return
     */
    public static  <E>E createOrThrow(Class<E> clazz,Object v)  {
        try {
            Method values = clazz.getMethod("values");
            IEnum inter[] = (IEnum[]) values.invoke(null,null);
            for (IEnum em:inter) {
                Object value =  em.getValue();
                if(StrUtil.equals(value+"",v+"")) {
                    return (E)em;
                }
            }
        } catch (Exception e) {
            LogUtil.error(log, BaseExceptionEnum.ERROR,ExceptionUtil.getMessage(e));
            e.printStackTrace();
        }
        LogUtil.error(log,BaseExceptionEnum.FAIL,clazz.getName()+"对应枚举转换异常，value:"+v);
        throw new MccBizException(BaseExceptionEnum.FAIL);
    }

    /**
     * 通过枚举的类和value值得到相应的枚举，没有则抛出异常
     * @param clazz
     * @param v
     * @param <E>
     * @return
     */
    public static  <E extends Enum<E>>E create(Class<E> clazz,Object v)  {
        try {
            Method values = clazz.getMethod("values");
            IEnum inter[] = (IEnum[]) values.invoke(null,null);
            for (IEnum em:inter) {
                Object value =  em.getValue();
                if(StrUtil.equals(value+"",v+"")) {
                    return (E)em;
                }
            }
        } catch (Exception e) {
            LogUtil.error(log, BaseExceptionEnum.ERROR,ExceptionUtil.getMessage(e));
            e.printStackTrace();
        }
        LogUtil.warn(log,BaseExceptionEnum.FAIL.getCode(),clazz.getName()+"对应枚举转换异常，value:"+v);
        return EnumUtil.fromString(clazz,"NOT_FOUND");
    }

    /**
     * 解析枚举
     * @param clazz
     * @return
     */
    private static Map<String,Map<Object,String>> analysisEnum(String name,Class clazz)  {
        Map<String,Map<Object,String>> enumMaps = MapUtil.newHashMap();
        try {
            Method  values = clazz.getMethod("values");
            Field fields[] = clazz.getDeclaredFields();
            int i = 0;
            for(Field field:fields) {
                if(StrUtil.contains(field.getName(),"desc")) {
                    IEnum inter[] = (IEnum[]) values.invoke(null,null);
                    Map<Object,String> map = MapUtil.newHashMap();
                    for (IEnum em:inter) {
                        Object key =  em.getValue();
                        if(ObjectUtil.isNotNull(key)) {
                            Method  desc = clazz.getMethod("get"+StrUtil.upperFirst(field.getName()));
                            String value = (String) desc.invoke(em,null);
                            map.put(key,value);
                        }
                    }
                    String keyName = i>0?name+i:name;
                    enumMaps.put(keyName,map);
                    i++;
                }
            }
        } catch (Exception e) {
            LogUtil.error(log, BaseExceptionEnum.ERROR,"出错枚举："+clazz.getName());
            e.printStackTrace();
        }
        return enumMaps;
    }
}
